package run

import (
	"bufio"
	"codejy/analysis"
	"codejy/analysis/lineanalysis"
	"codejy/definition"
	"codejy/globalfunc"
	"codejy/linestrategy"
	"codejy/store"
	"codejy/structdef"
	"codejy/typedefault"
	"codejy/typedefault/thread"
	"codejy/utils"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func init() {
	globalfunc.RegisterFuncAll("RunCmdLineCode", RunCmdLineCode)
	globalfunc.RegisterFuncAll("AnonymousFunctionToFunctionDefine", AnonymousFunctionToFunctionDefine)
	globalfunc.RegisterFuncAll("OptimizeForVarDeclare", OptimizeForVarDeclare)
}

func CodeScanner(filePath string, parent string, isMain bool) {

	var fileName, filePath1 = GetAliasOrFileName(filePath) //文件名或者别名 ,不带后缀
	filePath = filePath1                                   //实际文件名,去除别名了
	//判断文件后缀是否为.jy
	if !strings.HasSuffix(filePath, ".jy") {
		panic("文件后缀必须为.jy")
	}
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	//读取文件内容,转换为行代码
	codeLines := readFileLineCode(file)
	codes := structdef.ToLineCode(codeLines, filePath)
	//去除注释和空格,验证赋值变量名称是否合法
	codes = globalfunc.ExecuteFunc("SkipCommentAndEmpty", codes).([]structdef.LineCodeDefinition)
	//分析文件,函数和类型定义，和匿名函数
	fileLineCodes := structdef.AnalyseTypeOrFunLineCode(codes, filePath)

	//当前操作文件   ,因为后面的代码解析都需要将内容解析到对应的文件环境中
	store.MainThreadStore.HandleFilePath = filePath
	//设置当前操作环境
	store.AddCurrentEnv(store.MainThreadStore, fileName)
	store.AddGlobalEnv(store.MainThreadStore, store.MainThreadStore.CurrentEnv)
	store.AddParentEnv(store.MainThreadStore, store.MainThreadStore.CurrentEnv)

	//匿名函数转换为函数定义
	fileLineCodes = AnonymousFunctionToFunctionDefine(fileLineCodes)
	//优化循环变量声明
	fileLineCodes = OptimizeForVarDeclare(fileLineCodes)

	//获取函数和类型定义,装载到内存中
	definition.DefanalysisMode(&fileLineCodes, filePath)
	//添加以加载的文件
	store.LoadedFile[filePath] = false
	//递归处理导入文件
	importPackage := getImportFile(&fileLineCodes)
	for i := range importPackage {
		path := importPackage[i]
		//实际文件名,去除别名了
		fileName2, notasFilePath := GetAliasOrFileName(path)
		//做映射
		store.AddImportFile(filePath, fileName2, notasFilePath)
		//判断是否已经加载过
		_, p := store.LoadedFile[notasFilePath]
		if p {
			continue
		}
		CodeScanner(path, filePath, isMain)
	}

	//非主文件不运行代码,只加载定义
	if store.MainFilePath != filePath {
		return
	}
	//做主文件的映射
	store.AddImportFile(filePath, fileName, filePath)

	//切换到主文件执行
	//当前操作文件
	store.MainThreadStore.HandleFilePath = filePath
	//设置当前操作环境
	store.AddCurrentEnv(store.MainThreadStore, fileName)
	store.AddGlobalEnv(store.MainThreadStore, store.MainThreadStore.CurrentEnv)
	store.AddParentEnv(store.MainThreadStore, store.MainThreadStore.CurrentEnv)

	//装载底层核心
	globalfunc.ExecuteFunc("InjectTypeDefault", filePath)
	//执行代码
	if isMain {
		//计算代码执行时间
		start := time.Now()
		//分析代码
		fileLineCodes = structdef.AnalyseLineCodeDefinitions(store.MainThreadStore, fileLineCodes, filePath)
		// 策略模式,处理各种代码
		currentLineCodesLength := len(fileLineCodes)
		for i := 0; i < currentLineCodesLength; i++ {
			//分析当前操作行代码
			definition := structdef.AnalyseLineCodeDefinition(store.MainThreadStore, fileLineCodes[i], filePath)
			//修改fileLineCodes
			fileLineCodes[i] = definition
			//设置当前操作文件所有行
			store.MainThreadStore.Codes = fileLineCodes
			// 设置当前操作指针
			store.MainThreadStore.CurrentPoint = i
			//设置当前操作行
			store.MainThreadStore.Code = fileLineCodes[i]
			//添加错误日志
			store.AddErrorLog(store.MainThreadStore, store.NewRecordLog(store.MainThreadStore, fileLineCodes[i])) //添加错误日志
			//策略模式,处理各种行代码
			utils.CodeExecutionTime("主文件代码耗时情况", store.MainThreadStore, func() {
				linestrategy.LineStrategyMode(store.MainThreadStore)
			})
			//跳过已经处理的行,调整指针位置
			i = store.MainThreadStore.CurrentPoint
			utils.LimitSpeed(store.MainThreadStore)
		}

		//主线程等待子线程执行完毕,否则主线程不会退出。这样保证了子线程的执行
		thread.ThreadAllWait.Wait()
		store.LoadedFile[fileName] = true //设置此文件已经加载完成
		//一共运行了多少行代码
		fmt.Println("一共运行了多少行代码:", utils.ChargeCodeLine)
		//打印执行时间过长的代码
		utils.PrintCodeExecutionTime(store.MainThreadStore)

		end := time.Now()
		seconds := end.Sub(start).Seconds()
		fmt.Println("代码总执行耗时:", seconds, "秒")
	}

}

// 按行读取文件内容
func readFileLineCode(file *os.File) []string {
	var returnData []string
	r := bufio.NewReader(file)
	for {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		returnData = append(returnData, string(line))
	}
	return returnData
}

// 获取所有导入包 ,并且删除,返回导入包的最后一行的下标
func getImportFile(codes *[]structdef.LineCodeDefinition) []string {
	var importPackages []string

	for i := 0; i < len(*codes); {
		//判断开头是否是import
		if strings.HasPrefix((*codes)[i].LineContent, "import") {
			//删除行
			*codes = append((*codes)[:i], (*codes)[i+1:]...)
			//持续添加直到大括号结束
			for j := i; j < len(*codes); {
				if strings.HasSuffix((*codes)[j].LineContent, "}") {
					//删除行
					*codes = append((*codes)[:j], (*codes)[j+1:]...)
					break
				}
				importPackages = append(importPackages, (*codes)[j].LineContent)
				//删除行
				*codes = append((*codes)[:j], (*codes)[j+1:]...)
			}
			//只处理第一个import
			break
		} else {
			i++
		}
	}

	for i := range importPackages {
		//如果导入的文件路径开头是没有@那么需要拼接主目录
		if !strings.HasPrefix(importPackages[i], "@") {
			importPackages[i] = filepath.Join(store.MainFileDir, importPackages[i])
		}
		//如果导入的文件路径开头是@那么需要拼接工作目录
		if strings.HasPrefix(importPackages[i], "@") {
			for i2 := range store.CurrentOtherDir {
				//拼接路径
				var path = filepath.Join((store.CurrentOtherDir)[i2], importPackages[i][1:])
				//判断文件是否存在
				_, err := os.Stat(path)
				if err == nil {
					fmt.Println("File exist")
					importPackages[i] = path
				}
			}
		}
	}

	return importPackages
}

// 在命令中执行一行代码,默认作用域,文件路径都是"@context@"
func RunCmdLineCode(threadStore *structdef.ThreadStore, lineCode string) {
	//当前操作文件
	threadStore.HandleFilePath = store.CmdDefaultEnv
	//设置当前环境
	if threadStore.CurrentEnv == "" {
		store.AddCurrentEnv(threadStore, store.CmdDefaultEnv)
		store.AddGlobalEnv(threadStore, threadStore.CurrentEnv)
		store.AddParentEnv(threadStore, threadStore.CurrentEnv)
	}
	//注入核心类型
	typedefault.InjectTypeDefault(threadStore.HandleFilePath)
	if lineCode == "" {
		return
	}
	//将单引号转换为双引号
	lineCode = strings.ReplaceAll(lineCode, "'", "\"")
	//去掉所有空格
	lineCode = strings.ReplaceAll(lineCode, " ", "")
	// 将代码行转换为LineCode
	fileLineCode := structdef.AnalyseLineCodeDefinitionsByCmd(threadStore, lineCode, threadStore.HandleFilePath)
	//如果是-1那么就是没有找到
	if fileLineCode.LineCodeType == -1 {
		panic(lineCode + " 语法错误")
	}
	//设置当前操作文件所有行
	threadStore.Codes = []structdef.LineCodeDefinition{fileLineCode}
	// 设置当前操作指针
	threadStore.CurrentPoint = 0
	//设置当前操作行
	threadStore.Code = fileLineCode
	//添加错误日志
	store.AddErrorLog(threadStore, store.NewRecordLog(threadStore, fileLineCode)) //添加错误日志
	linestrategy.LineStrategyMode(threadStore)                                    //策略模式,处理各种行代码
	//主线程等待子线程执行完毕,否则主线程不会退出。这样保证了子线程的执行
	thread.ThreadAllWait.Wait()
}

// 获取别名或者文件名,返回去除别名的路径
func GetAliasOrFileName(filePath string) (string, string) {
	var fileName string //文件名或者别名 ,不带后缀
	//判断是否有as存在
	if strings.Contains(filePath, "as") {
		//获取as后面的文件名别名
		fileName = filePath[strings.LastIndex(filePath, "as")+2:]
		//删除as和后面的文件名别名
		filePath = filePath[0:strings.LastIndex(filePath, "as")]
		//去空格
		filePath = strings.TrimSpace(filePath)
		//fileName去空格
		fileName = strings.TrimSpace(fileName)
	}
	//判断文件后缀是否为.jy
	if !strings.HasSuffix(filePath, ".jy") {
		panic("文件后缀必须为.jy")
	}
	//如果不存在别名的情况,那么获取真实文件名称,不带后缀
	if fileName == "" {
		stat, p := os.Stat(filePath)
		if p != nil {
			panic("文件不存在")
		}
		fileName = stat.Name()
		fileName = fileName[0:strings.LastIndex(fileName, ".")]
	}
	return fileName, filePath
}

// 优化循环内部变量声明,因为循环内部如果使用var 声明变量,那么每次循环都会重新声明一次,会导致变量名称重复
// 解决办法:
// 1. 在循环之前就声明变量
// 2. 取消循环内部的变量声明 改为变量赋值
func OptimizeForVarDeclare(codeBlocks []structdef.LineCodeDefinition) []structdef.LineCodeDefinition {
	for i := 0; i < len(codeBlocks); i++ {
		codeDefinition := codeBlocks[i]
		//判断是否是for语句
		if analysis.IsFor(codeDefinition.LineContent) {
			var forIndex = i //for语句的下标
			end := linestrategy.GetForCodeBlockAllEnd(codeBlocks, i+1)
			//找var语言
			var defNum = 0
			var j int
			for j = i; j < end; j++ {
				codeDefinition1 := codeBlocks[j]
				//判断是否是var语句
				if analysis.IsVariableDefine(codeDefinition1.LineContent) {
					//获取变量名称
					var variableName string
					//判断是否有等号
					index := strings.Index(codeDefinition1.LineContent, "=")
					if index == -1 {
						//没有等号去除var
						variableName = codeDefinition1.LineContent[3:]
					}
					//有等号去除var和等号
					variableName = codeDefinition1.LineContent[3:index]
					//拼接变量声明
					var defVar = "var" + variableName
					//去掉所有空格
					defVar = strings.ReplaceAll(defVar, " ", "")
					//将新的变量声明插入到for语句的前面
					codeBlocks = append(codeBlocks[:forIndex], append([]structdef.LineCodeDefinition{{
						LineContent:  defVar,
						LineCodeType: lineanalysis.VariableDefinition,
						LineNum:      codeDefinition1.LineNum,
						LineCodeFile: codeDefinition1.LineCodeFile,
					}}, codeBlocks[forIndex:]...)...)
					defNum++
					j++
					forIndex++
					//去掉当前行的var
					codeBlocks[j].LineContent = codeDefinition1.LineContent[3:]

				}
			}
			//跳过for语句
			i = j + defNum
		}
	}
	return codeBlocks
}

// 匿名函数本质上是一个函数定义,只是在底层会将匿名函数转换为函数定义,并给函数定义一个随机名称，然后在原来的位置放入生成的函数名称
func AnonymousFunctionToFunctionDefine(codeBlocks []structdef.LineCodeDefinition) []structdef.LineCodeDefinition {
	for i := 0; i < len(codeBlocks); i++ {
		codeDefinition := (codeBlocks)[i]
		//判断是否是匿名函数
		if analysis.IsAnonymousFunctionDefine(codeDefinition.LineContent) {
			var codelines []structdef.LineCodeDefinition
			var start = i
			var end = 0
			//截取函数名
			funcName := "@AnonymousFunc@" + utils.RandStringRunes()
			//跳过行
			i++
			//解析函数定义 ,从函数定义开始，往下找到函数体结束
			var num = 1
			var endContent structdef.LineCodeDefinition
			for j := i; j < len(codeBlocks); {
				currentLineCode := codeBlocks[j]
				//判断匿名函数内部是否还有匿名函数
				if analysis.IsAnonymousFunctionDefine(currentLineCode.LineContent) {
					//截取函数
					var codelines1 []structdef.LineCodeDefinition
					var num1 = 1
					var end1 = j
					for j1 := j; j1 < len(codeBlocks); {
						currentLineCode1 := codeBlocks[j1]
						if strings.HasSuffix(currentLineCode1.LineContent, "{") {
							num1++
						}
						if strings.HasPrefix(currentLineCode1.LineContent, "}") {
							num1--
						}
						if num1 == 0 {
							//跳过}
							j1++
							end1 = j1
							break
						}
						codelines1 = append(codelines1, currentLineCode1)
						j1++
					}
					//递归调用
					define := AnonymousFunctionToFunctionDefine(codelines1)
					//删除匿名函数
					codeBlocks = append(codeBlocks[:j], codeBlocks[end1-1:]...)
					//插入到当前位置
					codeBlocks = append(codeBlocks[:j], append(define, codeBlocks[j:]...)...)
					continue
				}
				codelines = append(codelines, currentLineCode)
				var pd = true
				if strings.HasSuffix(currentLineCode.LineContent, ",func{") {
					num = 0
					pd = false
				}
				if pd && strings.HasSuffix(currentLineCode.LineContent, "{") {
					num++
				}
				if pd && strings.HasPrefix(currentLineCode.LineContent, "}") { //可能是}或者}()
					num--
				}
				if pd {
					if num == 0 {
						endContent = currentLineCode
					}
					//如果不是,func{那么就删除跳过的行
					codeBlocks = append(codeBlocks[:j], codeBlocks[j+1:]...)
				}
				if num == 0 { //函数体结束
					//跳过最后}
					codelines = codelines[:len(codelines)-1]
					codelines = structdef.AnalyseLineCodeDefinitions(store.MainThreadStore, codelines, store.MainThreadStore.HandleFilePath)
					codelines = OptimizeForVarDeclare(codelines) //优化for循环中的变量声明
					var annotations = make(map[string]map[string]string)
					store.RegisterFunDefinition(store.MainThreadStore.HandleFilePath, funcName, structdef.NewFuncDefinition(funcName, codelines, annotations))
					end = j
					break
				}

			}
			//将endContent插入到end前面
			codeBlocks = append(codeBlocks[:end], append([]structdef.LineCodeDefinition{endContent}, codeBlocks[end:]...)...)
			//1.判断结尾是}或者}(  如果是}那么删除这一行如果是}(那么将()取出来放入到funcName后面
			var pa = true
			if strings.HasPrefix(codeBlocks[end].LineContent, "}(") {
				//取出()放入到funcName后面
				funcName = funcName + codeBlocks[end].LineContent[1:]
				codeBlocks = append(codeBlocks[:end], codeBlocks[end+1:]...)
				pa = false
			}

			if pa && strings.HasPrefix(codeBlocks[end].LineContent, "})") {
				//补充)
				funcName = funcName + ")"
				codeBlocks = append(codeBlocks[:end], codeBlocks[end+1:]...)
				pa = false
			}
			if pa && strings.HasPrefix(codeBlocks[end].LineContent, "},func{") {
				//补充)
				funcName = funcName + ",func{"
				codeBlocks = append(codeBlocks[:end], codeBlocks[end+1:]...)
				pa = false
			}
			if pa && strings.HasSuffix(codeBlocks[end].LineContent, ",func{") {
				//补充)
				funcName = funcName + codeBlocks[end].LineContent[1:]
				codeBlocks = append(codeBlocks[:end], codeBlocks[end+1:]...)
				pa = false
			}
			if pa && strings.HasPrefix(codeBlocks[end].LineContent, "}") {
				funcName = funcName + codeBlocks[end].LineContent[1:]
				codeBlocks = append(codeBlocks[:end], codeBlocks[end+1:]...)
			}
			//2.将开头位置的func{替换为funcName
			codeBlocks[start].LineContent = strings.ReplaceAll(codeBlocks[start].LineContent, "func{", funcName)
			i = start - 1

		}
	}
	return codeBlocks
}
